package com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.repository;

import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import com.alibaba.algo.cooperation.common.BaseResult;
import com.alibaba.algo.cooperation.feign.service.UserFeignService;
import com.alibaba.algo.cooperation.feign.vo.req.SelectUserInfoReqVO;
import com.alibaba.algo.cooperation.feign.vo.res.RpcUserInfoVO;
import com.alibaba.algo.cooperation.feign.vo.res.UserEmployeeResVO;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.context.ModelDeploymentContext;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.*;
import com.iwhalecloud.citybrain.flow.platform.manager.application.common.threadlocal.DeployIdContainer;
import com.iwhalecloud.citybrain.flow.platform.manager.application.service.*;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.DateUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.FlowUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.FormUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.JsonUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.ModelUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.application.utils.StringUtils;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ConstanceVariable;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ErrorCode;
import com.iwhalecloud.citybrain.flow.platform.manager.common.ResponseResult;
import com.iwhalecloud.citybrain.flow.platform.manager.common.client.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.CommentTypeEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.enums.NodeAuthEnum;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.*;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormCreateVO;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.form.FormInstanceDto;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.monitor.FlowTaskTraceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.monitor.ProcessInstanceActionVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.monitor.ProcessInstanceDetailBaseInfoVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.BatchTurnTaskVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.TaskInfoVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.TaskMultiInstanceVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.task.TurnTaskVo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.model.userentity.UserInfo;
import com.iwhalecloud.citybrain.flow.platform.manager.domain.repository.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.common.converter.*;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.common.exception.BusiException;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowExecutionQueryDAO;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowFormExternalInstanceDAO;
import com.iwhalecloud.citybrain.flow.platform.manager.infrastructure.mapper.FlowTaskTraceDAO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.MultiInstanceLoopCharacteristics;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.UserTask;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.editor.language.json.converter.BpmnJsonConverterUtil;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.RuntimeServiceImpl;
import org.flowable.engine.impl.dynamic.DynamicUserTaskBuilder;
import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl;
import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.flowable.engine.impl.persistence.entity.ResourceEntityImpl;
import org.flowable.engine.impl.runtime.ChangeActivityStateBuilderImpl;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.form.api.FormInfo;
import org.flowable.form.model.SimpleFormModel;
import org.flowable.image.impl.DefaultProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import static com.iwhalecloud.citybrain.flow.platform.manager.application.common.enums.ProcessHandleEnum.fromCode;

/**
 * @author cheng.huaxing
 * @date 2019-01-24
 */
@Slf4j
@Service
public class FlowProcessServiceImpl implements FlowProcessService {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private ResourcePersistRepository resourcePersistRepository;
    @Autowired
    private FlowProcessInstanceOperateTraceRepository flowProcessInstanceOperateTraceRepository;
    @Autowired
    private FlowTaskTraceDAO flowTaskTraceDAO;
    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private FlowExecutionQueryDAO flowExecutionQueryDAO;

    @Autowired
    private FlowAppRepository flowAppRepository;

    @Autowired
    private FlowParticipantRepository flowParticipantRepository;

    @Autowired
    private FlowActivityJumpHistoryRepository flowActivityJumpHistoryRepository;

    @Autowired
    private FlowProcessInstanceTraceRepository flowProcessInstanceTraceRepository;

    @Autowired
    private RemindCheckService remindCheckService;

    @Autowired
    private FlowFormInstanceRepository formInstanceRepository;

    @Autowired
    private FlowReNodeAuthRepository nodeAuthRepository;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private FlowFormRepository flowFormRepository;

    @Autowired
    FlowFormExternalInstanceDAO flowFormExternalInstanceDAO;

    @Autowired
    private FlowReModelCustomConfigRepository flowReModelCustomConfigRepository;

    @Autowired
    private ProcessInstanceMonitorService processInstanceMonitorService;

    @Autowired
    private UserFeignService userFeignService;

    @Autowired
    private FlowModelVariableService flowModelVariableService;

    @Autowired
    protected FlowTaskTraceRepository flowTaskTraceRepository;

    @Autowired
    private NodeCandidateService nodeCandidateService;

    @Autowired
    private FlowBaseDataHandleProcessor flowBaseDataHandleProcessor;

    @Autowired
    private ProcessEngine processEngine;

    @Autowired
    private List<ModelDeploymentPostProcessor> modelDeploymentPostProcessors;

    @Autowired
    private FlowTaskService flowTaskService;

    @Autowired
    private FlowTurnTaskService flowTurnTaskService;


    @Override
    public ResponseResult list(ProcessQueryVo processQueryVo) {
        User current = userRepository.getCurrentUser();
        if (!UserEnum.SUPER.type().equals(current.getUserType())) {
            boolean isAssociated = flowAppRepository.verifyAppAuthorized(current, processQueryVo.getAppId(), processQueryVo.getBusiId());
            if (!isAssociated) {
                return ResponseResult.success(getEmptyProcess());
            }
        }

        if (Objects.isNull(processQueryVo.getProcessStatus())) {
            return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        switch (processQueryVo.getProcessStatus()) {
            case "executing":
                Map<String, Object> processMap = listExecutingProcess(processQueryVo);
                return ResponseResult.success(processMap).setTotalCount(Integer.parseInt(processMap.get("executingTotal").toString()));
            case "abnormal":
                processMap = listAbnormalProcess(processQueryVo);
                return ResponseResult.success(processMap).setTotalCount(Integer.parseInt(processMap.get("abnormalTotal").toString()));
            case "completed":
                processMap = listCompletedProcess(processQueryVo);
                return ResponseResult.success(processMap).setTotalCount(Integer.parseInt(processMap.get("finishTotal").toString()));
            default:
                return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
    }

    private Map<String, Object> getEmptyProcess() {
        Map<String, Object> processMap = new HashMap<>(8);
        processMap.put("process", new ArrayList<>());
        processMap.put("abnormalTotal", 0);
        processMap.put("executingTotal", 0);
        processMap.put("finishTotal", 0);
        return processMap;
    }

    private Map<String, Object> listAbnormalProcess(ProcessQueryVo processQueryVo) {
        Map<String, Object> processMap = new HashMap<>(8);

        //查询进行中流程
        int executingTotal = getExecutingTotal(processQueryVo);
        int finishTotal = getFinishTotal(processQueryVo);
        //获取已完成的流程实例列表
        PageHelper.startPage(processQueryVo.getPageNum(), processQueryVo.getPageSize());
        List<FlowExecutionQueryInfo> executionInfos = flowExecutionQueryDAO.listAbnormalProcess(processQueryVo);
        PageInfo<FlowExecutionQueryInfo> pageInfo = new PageInfo<>(executionInfos);
        //resolveUserInfo(executionInfos);


        processMap.put("process", executionInfos);
        processMap.put("abnormalTotal", pageInfo.getTotal());
        processMap.put("executingTotal", executingTotal);
        processMap.put("finishTotal", finishTotal);

        return processMap;
    }

    private Map<String, Object> listCompletedProcess(ProcessQueryVo processQueryVo) {
        Map<String, Object> processMap = new HashMap<>(8);

        //查询进行中流程
        int executingTotal = getExecutingTotal(processQueryVo);
        int abnormalTotal = getAbnormalTotal(processQueryVo);

        //获取已完成的流程实例列表
        PageHelper.startPage(processQueryVo.getPageNum(), processQueryVo.getPageSize());
        List<FlowExecutionQueryInfo> executionInfos = flowExecutionQueryDAO.listCompletedProcess(processQueryVo);
        PageInfo<FlowExecutionQueryInfo> pageInfo = new PageInfo<>(executionInfos);


        processMap.put("process", executionInfos);
        processMap.put("abnormalTotal", abnormalTotal);
        processMap.put("executingTotal", executingTotal);
        processMap.put("finishTotal", pageInfo.getTotal());

        return processMap;
    }

    private int getExecutingTotal(ProcessQueryVo processQueryVo) {
        //屏蔽掉PageInterceptor插件自动执行count查询
        PageHelper.startPage(1, 1, false);
        return flowExecutionQueryDAO.countExecutingProcess(processQueryVo);
    }

    /**
     * 执行中流程列表
     */
    private Map<String, Object> listExecutingProcess(ProcessQueryVo processQueryVo) {
        Map<String, Object> processMap = new HashMap<>(8);

        //查询已完成流程
        int finishTotal = getFinishTotal(processQueryVo);
        int abnormalTotal = getAbnormalTotal(processQueryVo);

        //获取运行中的流程实例列表
        PageHelper.startPage(processQueryVo.getPageNum(), processQueryVo.getPageSize());
        List<FlowExecutionQueryInfo> executionInfos = flowExecutionQueryDAO.listExecutingProcess(processQueryVo);
        PageInfo<FlowExecutionQueryInfo> pageInfo = new PageInfo<>(executionInfos);
        //resolveUserInfo(executionInfos);


        processMap.put("process", executionInfos);
        processMap.put("abnormalTotal", abnormalTotal);
        processMap.put("executingTotal", pageInfo.getTotal());
        processMap.put("finishTotal", finishTotal);
        return processMap;
    }

    private int getAbnormalTotal(ProcessQueryVo processQueryVo) {
        PageHelper.startPage(1, 1, false);
        return flowExecutionQueryDAO.countAbnormalProcess(processQueryVo);
    }

    private int getFinishTotal(ProcessQueryVo processQueryVo) {
        PageHelper.startPage(1, 1, false);
        return flowExecutionQueryDAO.countCompletedProcess(processQueryVo);
    }


    @Override
    public ResponseResult handleProcessInstance(ProcessHandleVo processHandleVo) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processHandleVo.getProcessInstanceId()).singleResult();
        if (Objects.isNull(processInstance)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "流程不存在或已完成");
        }
        ProcessHandleEnum handleEnum = fromCode(processHandleVo.getProcessStatus());
        if (handleEnum == null) {
            return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        switch (handleEnum) {
            case SUSPEND:
                if (processInstance.isSuspended()) {
                    return ResponseResult.fail(ErrorCode.PROCESS_SUSPEND_ERROR.errCode(), ErrorCode.PROCESS_SUSPEND_ERROR.errMessage());
                }
                taskService.addComment(null, processInstance.getProcessInstanceId(), "suspendComment", processHandleVo.getComment());
                runtimeService.suspendProcessInstanceById(processHandleVo.getProcessInstanceId());
                break;
            case ACTIVATE:
                if (!processInstance.isSuspended()) {
                    return ResponseResult.fail(ErrorCode.PROCESS_ACTIVATE_ERROR.errCode(), ErrorCode.PROCESS_ACTIVATE_ERROR.errMessage());
                }
                runtimeService.activateProcessInstanceById(processHandleVo.getProcessInstanceId());
                taskService.addComment(null, processInstance.getProcessInstanceId(), "activateComment", processHandleVo.getComment());
                break;
            case CANCEL:
                if (processInstance.isSuspended()) {
                    return ResponseResult.fail(ErrorCode.PROCESS_SUSPEND_ERROR.errCode(), ErrorCode.PROCESS_SUSPEND_ERROR.errMessage());
                }
                // 获取开始节点
                List<String> currentActivityIdList = FlowUtils.getInstance().findCurrFlowAllExecutionId(processInstance.getProcessInstanceId());
                ChangeActivityStateBuilderImpl changeActivityStateBuilder = new ChangeActivityStateBuilderImpl((RuntimeServiceImpl) runtimeService);
                changeActivityStateBuilder.processInstanceId(processInstance.getProcessInstanceId())
                        .moveExecutionsToSingleActivityId(currentActivityIdList, ((ExecutionEntityImpl) processInstance).getStartActivityId())
                        .changeState();
                taskService.addComment(null, processInstance.getProcessInstanceId(), "moveComment", processHandleVo.getComment());
                break;
            case DELETE:
                if (processInstance.isSuspended()) {
                    return ResponseResult.fail(ErrorCode.PROCESS_SUSPEND_ERROR.errCode(), ErrorCode.PROCESS_SUSPEND_ERROR.errMessage());
                }
                taskService.addComment(null, processInstance.getProcessInstanceId(), "deleteComment", processHandleVo.getComment());
                runtimeService.deleteProcessInstance(processHandleVo.getProcessInstanceId(), processHandleVo.getComment());
                break;
            default:
                return ResponseResult.fail(ErrorCode.PARAMETER_ERROR.errCode(), ErrorCode.PARAMETER_ERROR.errMessage());
        }
        return ResponseResult.success();
    }

    @Override
    public ResponseResult getProcessInstanceDiagramAndInfo(String processInstanceId) {
        FlowProcessInstanceTraceVo processInstanceTrace = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        if (Objects.isNull(processInstanceTrace)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), "流程不存在!");
        }
        Map<String, Object> dataMap = new HashMap<>(4);
        //流程执行信息
        ProcessInstanceDetailBaseInfoVo baseInfo = new ProcessInstanceDetailBaseInfoVo();
        baseInfo.setProcessInstanceId(processInstanceId);
        baseInfo.setModelName(processInstanceTrace.getModelName());
        baseInfo.setApplyTime(processInstanceTrace.getStartTime());
        baseInfo.setApplyUserName(processInstanceTrace.getStarterName());
        baseInfo.setApplyUserId(processInstanceTrace.getStarterId());
        baseInfo.setAppName(processInstanceTrace.getAppName());
        baseInfo.setBusiName(processInstanceTrace.getBusiName());
        baseInfo.setStatus(processInstanceTrace.getStatus());
        baseInfo.setLinkStatus(processInstanceTrace.getLinkStatus());
        // 补全发起人信息
        this.resolveEmployeeCode(baseInfo);
        //催办：1、运行中的任务有配置开启催办 2、今天没有催办过
        //baseInfo.setCanRemind(resolveRemindTag(processInstanceTrace));
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstanceTrace.getProcessDefinitionId()).singleResult();
        //添加流程描述信息
        baseInfo.setDescription(processDefinition.getDescription());
        //获取流程资源
        InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName() + ".json");
        ProcessModelVo processModel = ModelUtils.getInstance().streamToModel(inputStream);
        Map<String, String> overrideIdAndResourceIdMap = new HashMap<>(8);
        if (processModel != null) {
            overrideIdAndResourceIdMap = getOverrideIdAndResourceIdMap(processModel);
        }

        //当前任务
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
        Set<String> taskSet = new HashSet<>(tasks.size());
        //设置是否可撤回，是否可催办，撤销：1、运行中的任务有配置开启了撤销 2、流程还在进行中。。
        //催办：1、运行中的任务有配置开启催办 2、今天没有催办过
        try {
            resolveRemindAndRecallTag(baseInfo, tasks);
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (Task task : tasks) {
            if (overrideIdAndResourceIdMap.containsKey(task.getTaskDefinitionKey())) {
                taskSet.add(overrideIdAndResourceIdMap.get(task.getTaskDefinitionKey()));
            } else {
                taskSet.add(task.getTaskDefinitionKey());
            }
        }


        dataMap.put("model", ModelUtils.getInstance().transformToViewModel(processModel));
        dataMap.put("process", baseInfo);
        dataMap.put("currentTaskList", taskSet);
        return ResponseResult.success(dataMap);
    }

    private void resolveRemindAndRecallTag(ProcessInstanceDetailBaseInfoVo baseInfo, List<Task> runningTasks) {
        if (CollectionUtil.isEmpty(runningTasks)
                || !Objects.equals(userRepository.getCurrentUser().getUserCode(), baseInfo.getApplyUserId())) {
            //没有运行中的任务，说明流程已经结束了，则不可撤回，也不可催办
            //当前登录人不是流程发起人，则无权限操作
            baseInfo.setCanRecall(false);
            baseInfo.setCanRemind(false);
            return;
        }


        for (Task task : runningTasks) {
            //获取任务节点上的配置信息，只要找到有一个配置是开启的，则开启
            List<FlowReNodeAuthVo> nodeAuthList = nodeAuthRepository.listNodeAuthByProcdefIdAndNodeCode(task.getProcessDefinitionId(), task.getTaskDefinitionKey());
            Map<String, Boolean> authMap = nodeAuthList.stream().collect(Collectors.toMap(FlowReNodeAuthVo::getAuthName, FlowReNodeAuthVo::getAuthSwitch));
            Boolean canRecall = authMap.getOrDefault(NodeAuthEnum.CALLBACK.getCode(), false);
            if (canRecall) {
                baseInfo.setCanRecall(true);
            }
            Boolean canRemind = authMap.getOrDefault(NodeAuthEnum.REMIND.getCode(), false);
            if (canRemind) {
                baseInfo.setCanRemind(true);
            }
            if (baseInfo.isCanRecall() && baseInfo.isCanRemind()) {
                break;
            }
        }
        if (baseInfo.isCanRemind()) {
            //看看今天是否已经催办过。。。
            baseInfo.setCanRemind(!remindCheckService.hasRemindTag(baseInfo.getProcessInstanceId()));
        }
    }

    private boolean resolveRemindTag(FlowProcessInstanceTraceVo processInstanceTrace) {
        return !isDone(processInstanceTrace) && !remindCheckService.hasRemindTag(processInstanceTrace.getProcessInstanceId());
    }

    private boolean isDone(FlowProcessInstanceTraceVo processInstanceTrace) {
        return Objects.equals(ProcessInstanceLinkStatusEnum.END.getCode(), processInstanceTrace.getLinkStatus());
    }

    private Map<String, String> getOverrideIdAndResourceIdMap(ProcessModelVo processModel) {
        Map<String, String> overrideIdAndResourceIdMap = new HashMap<>(8);
        List<Object> childShapes = processModel.getChildShapes();
        for (Object childShape : childShapes) {
            String childShapeJsonStr = JsonUtils.toJson(childShape);
            JsonNode jsonNode = JsonUtils.readTree(childShapeJsonStr);
            if (jsonNode != null) {
                String type = jsonNode.get("stencil").get("id").asText();
                if (!"SequenceFlow".equals(type)) {
                    JsonNode properties = jsonNode.get("properties");
                    if (properties.get("overrideid") != null &&
                            StringUtils.isNotEmpty(properties.get("overrideid").asText())) {
                        overrideIdAndResourceIdMap.put(properties.get("overrideid").asText(), jsonNode.get("resourceId").asText());
                    }
                }
            }
        }
        return overrideIdAndResourceIdMap;
    }

    @Override
    public ResponseResult getProcessInstanceCurrentTasks(ProcessQueryVo processQueryVo) {
        int firstResult = (processQueryVo.getPageNum() - 1) * processQueryVo.getPageSize();
        int maxResults = processQueryVo.getPageNum() * processQueryVo.getPageSize();
        long taskCount = taskService.createTaskQuery().processInstanceId(processQueryVo.getProcessInstanceId()).count();
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(processQueryVo.getProcessInstanceId())
                .listPage(firstResult, maxResults);
        if (CollectionUtils.isEmpty(tasks)) {
            return ResponseResult.success();
        }
        // 保存会签任务节点
        Map<String, String> multiTaskMap = new HashMap<>(4);
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processQueryVo.getProcessInstanceId()).singleResult();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(processInstance.getDeploymentId()).singleResult();
        Collection<FlowElement> flowElements = repositoryService.getBpmnModel(processDefinition.getId()).getMainProcess().getFlowElements();
        for (FlowElement flowElement : flowElements) {
            if (flowElement instanceof UserTask) {
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = ((UserTask) flowElement).getLoopCharacteristics();
                if (Objects.nonNull(multiInstanceLoopCharacteristics)) {
                    multiTaskMap.put(flowElement.getId(), flowElement.getName());
                }
            }
        }
        List<String> taskIdList = new ArrayList<>(tasks.size());
        List<TaskInfoVo> taskInfoVoList = new ArrayList<>(tasks.size());
        for (Task task : tasks) {
            if (taskIdList.contains(task.getTaskDefinitionKey())) {
                continue;
            }
            FlowParticipantInfoVo participant = flowParticipantRepository.findByParticipantId(task.getAssignee());
            // 判断是否会签任务
            TaskInfoVo taskInfoVo = new TaskInfoVo();
            taskInfoVo.setTaskType("userTask");
            taskInfoVo.setTaskName(task.getName());
            taskInfoVo.setCreateTime(task.getCreateTime());
            if (Objects.nonNull(participant)) {
                taskInfoVo.setAssignee(participant.getParticipantName());
            }
            if (multiTaskMap.containsKey(task.getTaskDefinitionKey())) {
                taskInfoVo.setTaskType("multiUserTask");
                // 获取会签信息
                taskInfoVo.setMultiInstance((TaskMultiInstanceVo) getProcessInstanceMutiTaskInfo(task.getId(), task.getProcessInstanceId()).getDatas());
                taskIdList.add(task.getTaskDefinitionKey());
            }
            taskInfoVo.setTaskId(task.getId());
            taskInfoVo.setProcessInstanceId(task.getProcessInstanceId());
            taskInfoVoList.add(taskInfoVo);
        }
        ResponseResult responseResult = ResponseResult.success(taskInfoVoList);
        responseResult.setTotalCount((int) taskCount);
        responseResult.setPageNum(processQueryVo.getPageNum());
        responseResult.setPageSize(processQueryVo.getPageSize());
        return responseResult;
    }

    @Override
    public ResponseResult getProcessInstanceHandleLogs(ProcessQueryVo processQueryVo) {
        int firstResult = (processQueryVo.getPageNum() - 1) * processQueryVo.getPageSize();
        int maxResults = processQueryVo.getPageNum() * processQueryVo.getPageSize();
        List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().finished()
                .processInstanceId(processQueryVo.getProcessInstanceId())
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .listPage(firstResult, maxResults);
        long count = historyService.createHistoricTaskInstanceQuery().finished()
                .processInstanceId(processQueryVo.getProcessInstanceId())
                .count();
        List<Object> taskLogList = new ArrayList<>(historicTaskInstances.size());
        for (HistoricTaskInstance historicTaskInstance : historicTaskInstances) {
            Map<String, Object> historicTaskMap = new HashMap<>(4);
            ///List<Comment> comments = taskService.getTaskComments(historicTaskInstance.getId());
            FlowParticipantInfoVo participant = flowParticipantRepository.findByParticipantId(historicTaskInstance.getAssignee());
            if (Objects.isNull(participant)) {
                historicTaskMap.put("assignee", "");
            } else {
                historicTaskMap.put("assignee", participant.getParticipantName());
            }
            historicTaskMap.put("endTime", DateUtils.dateToString(historicTaskInstance.getEndTime()));
            historicTaskMap.put("taskName", historicTaskInstance.getName());
            historicTaskMap.put("type", "normal");
            FlowActivityJumpHistoryVo jumpHistory = resolveJumpHistory(historicTaskInstance.getId());
            if (Objects.nonNull(jumpHistory)) {
                //跳转
                historicTaskMap.put("type", "rollback");
                Map<String, Object> logMap = new HashMap<>(2);
                logMap.put("rollbackInfo", jumpHistory);
                historicTaskMap.put("handleDetail", logMap);

            } else {
                //非跳转的情况下
                if (StringUtils.isNotEmpty(historicTaskInstance.getFormKey())) {
                    //有表单
                    FormInfo formInfo = taskService.getTaskFormModel(historicTaskInstance.getId());
                    SimpleFormModel simpleFormModel = (SimpleFormModel) formInfo.getFormModel();
                    FormCreateVO formCreateVO = new FormCreateVO();
                    BeanUtils.copyProperties(simpleFormModel, formCreateVO);
                    formCreateVO.setFields(FormUtils.getInstance().transformToViewForm(simpleFormModel.getFields()));
                    Map<String, Object> logMap = new HashMap<>(2);
                    logMap.put("formInfo", formCreateVO);
                    historicTaskMap.put("handleDetail", logMap);
                } else {
                    //没表单
                    List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().taskId(historicTaskInstance.getId()).list();
                    Map<String, Object> logMap = new HashMap<>(2);
                    for (HistoricVariableInstance variableInstance : variableInstances) {
                        logMap.put(variableInstance.getVariableName(), variableInstance.getValue());
                    }
                    historicTaskMap.put("handleDetail", logMap);
                }
            }
            taskLogList.add(historicTaskMap);
        }
        ResponseResult responseResult = ResponseResult.success(taskLogList);
        responseResult.setTotalCount((int) count);
        responseResult.setPageNum(processQueryVo.getPageNum());
        responseResult.setPageSize(processQueryVo.getPageSize());
        return responseResult;
    }

    private FlowActivityJumpHistoryVo resolveJumpHistory(String taskId) {
        return flowActivityJumpHistoryRepository.getJumpHistoryByTaskId(taskId);
    }


    @Override
    public ResponseResult getProcessInstanceMutiTaskInfo(String taskId, String processInstanceId) {
        Task task = taskService.createTaskQuery().taskId(taskId).processInstanceId(processInstanceId).singleResult();
        if (Objects.isNull(task)) {
            return ResponseResult.fail(ErrorCode.NOT_EXIST.errCode(), ErrorCode.NOT_EXIST.errMessage());
        }
        List<HistoricTaskInstance> taskInstances = historyService.createHistoricTaskInstanceQuery().taskDefinitionKey(task.getTaskDefinitionKey())
                .processInstanceId(processInstanceId)
                .orderByHistoricTaskInstanceEndTime()
                .desc()
                .list();
        TaskMultiInstanceVo multiInstanceVo = new TaskMultiInstanceVo();
        List<String> assigneeList = new ArrayList<>(taskInstances.size());
        List<String> completedUserList = new ArrayList<>(taskInstances.size());
        List<String> activeUserList = new ArrayList<>(taskInstances.size());
        List<Map<String, Object>> mutiInstanceList = new ArrayList<>(taskInstances.size());
        for (HistoricTaskInstance taskInstance : taskInstances) {
            if (Objects.nonNull(taskInstance.getDeleteReason())) {
                continue;
            }
            List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().taskId(taskInstance.getId())
                    .processInstanceId(processInstanceId)
                    .list();
            FlowParticipantInfoVo user = flowParticipantRepository.findByParticipantId(taskInstance.getAssignee());
            Map<String, Object> mutiMap = new HashMap<>(4);
            mutiMap.put("taskTime", taskInstance.getEndTime());
            if (Objects.nonNull(user)) {
                mutiMap.put("assignee", user.getParticipantName());
                assigneeList.add(user.getParticipantName());
                if (Objects.isNull(taskInstance.getEndTime())) {
                    activeUserList.add(user.getParticipantName());
                } else {
                    completedUserList.add(user.getParticipantName());
                }
            }
            if (Objects.isNull(taskInstance.getEndTime())) {
                continue;
            }
            for (HistoricVariableInstance variableInstance : variableInstances) {
                if ("approval".equals(variableInstance.getVariableName())) {
                    mutiMap.put("taskResult", variableInstance.getValue());
                } else if ("comment".equals(variableInstance.getVariableName())) {
                    mutiMap.put("taskComment", variableInstance.getValue());
                }
            }
            mutiInstanceList.add(mutiMap);
        }
        multiInstanceVo.setAssigneeUserList(assigneeList);
        multiInstanceVo.setCompletedUserList(completedUserList);
        multiInstanceVo.setActiveUserList(activeUserList);
        multiInstanceVo.setMultiInstances(mutiInstanceList);

        Execution execution = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).executionId(task.getExecutionId()).singleResult();
        List<HistoricVariableInstance> variableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId)
                .executionId(execution.getParentId())
                .list();
        for (HistoricVariableInstance variableInstance : variableInstances) {
            if ("nrOfInstances".equals(variableInstance.getVariableName())) {
                multiInstanceVo.setInstances((int) variableInstance.getValue());
            } else if ("nrOfCompletedInstances".equals(variableInstance.getVariableName())) {
                multiInstanceVo.setCompletedInstances((int) variableInstance.getValue());
            } else if ("nrOfActiveInstances".equals(variableInstance.getVariableName())) {
                multiInstanceVo.setActiveInstances((int) variableInstance.getValue());
            }
        }

        return ResponseResult.success(multiInstanceVo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startProcess(String processDefinitionKey, Map<String, Object> variables) {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey)
                .active()
                .latestVersion()
                .singleResult();
        Assert.notNull(processDefinition);

        //设置流程变量中的默认值
        variables = flowModelVariableService.setDefaultValueToVariables(processDefinition.getDeploymentId(), variables);

        //发起流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
        if (processDefinition.hasStartFormKey() && MapUtil.isNotEmpty(variables)) {
            //将表单数据写入相应表中
            formInstanceRepository.insertFormInstance(resolveFormInstance(processInstance, variables));
        }
        //转移到PROCESS_STARTED全局事件中处理
        //flowReTimeoutSettingRepository.copyReTimeoutSetting(processInstance.getDeploymentId(), processInstance.getId(), null, null);
        return processInstance.getProcessInstanceId();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startProcessWithExternalForm(String processDefinitionKey, String formId) {
        //根据ProcessDefinitionKey拿到外置表单数据url
        FlowFormExternalInfoDomain flowFormExternalInfoDO = flowFormRepository.findExternalFormInfo(flowFormRepository.resolveFormKeyFromDefinitionKey(processDefinitionKey));
        //从第三方应用拿到用户填写的变量
        Map<String, Object> variables = restTemplate.getForObject(flowFormExternalInfoDO.getGetDataUrl() + "?formId=" + formId, Map.class);
        //发起流程
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
        //将流程实例和表单id写入数据库
        FlowFormExternalInstanceDO flowFormExternalInstanceDO = new FlowFormExternalInstanceDO();
        flowFormExternalInstanceDO.setFormId(formId);
        flowFormExternalInstanceDO.setProcessInstanceId(processInstance.getId());
        //根据processDefinitionKey获取表单key
        String formKey = flowFormRepository.resolveFormKeyFromDefinitionKey(processDefinitionKey);
        flowFormExternalInstanceDO.setFormKey(formKey);
        flowFormExternalInstanceDAO.insertExternalInstance(flowFormExternalInstanceDO);
        return processInstance.getProcessInstanceId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean cancelProcessInstance(HttpCancelProcessInstanceDto httpCancelProcessInstanceDto) throws Exception {
        if (StringUtils.isBlank(httpCancelProcessInstanceDto.getProcessInstanceId())) {
            throw new Exception("需要撤销的流程实例单号不能为空");
        }
        if (StringUtils.isBlank(httpCancelProcessInstanceDto.getUserCode())) {
            throw new Exception("流程撤销人员信息不能为空");
        }

        boolean hasStarterLimit = checkHasStarterLimit(httpCancelProcessInstanceDto.getProcessInstanceId(), httpCancelProcessInstanceDto.getUserCode());
        if (hasStarterLimit) {
            throw new Exception("非流程发起人不可撤销");
        }

        Set<String> processInstanceIdSet = new HashSet<>();
        processInstanceIdSet.add(httpCancelProcessInstanceDto.getProcessInstanceId());

        ProcessInstanceActionVo processInstanceActionVo = new ProcessInstanceActionVo();
        processInstanceActionVo.setAction(ProcessInstanceActionEnum.RECALL.getCode());
        processInstanceActionVo.setProcessInstanceIds(processInstanceIdSet);
        processInstanceActionVo.setUserCode(httpCancelProcessInstanceDto.getUserCode());
        processInstanceActionVo.setUserName(httpCancelProcessInstanceDto.getUserName());
        processInstanceActionVo.setComment(httpCancelProcessInstanceDto.getComment());

        return processInstanceMonitorService.doActionWithoutUserCheck(processInstanceActionVo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean handleProcessInstanceByAction(HttpProcessHandleDto httpProcessHandleDto) throws Exception {
        if (StringUtils.isBlank(httpProcessHandleDto.getProcessInstanceId())) {
            throw new BusiException("流程实例ID不能为空");
        }

        if (StringUtils.isBlank(httpProcessHandleDto.getUserCode())) {
            throw new BusiException("人员编码不能为空");
        }

        if (StringUtils.isBlank(httpProcessHandleDto.getAction())) {
            throw new BusiException("操作编码不能为空");
        }

        if (Objects.isNull(ProcessInstanceActionEnum.fromCode(httpProcessHandleDto.getAction()))) {
            throw new BusiException(ErrorCode.ACTION_NOT_EXIST);
        }

        ProcessInstance processInstance = flowBaseDataHandleProcessor.resolveProcessInstance(httpProcessHandleDto.getProcessInstanceId());
        if (Objects.isNull(processInstance)) {
            throw new BusiException(ErrorCode.PROCESS_INSTANCE_NOT_EXIST);
        }

        // 检查该流程实例当前的状态
        List<String> processInstanceIsList = new ArrayList<>();
        processInstanceIsList.add(httpProcessHandleDto.getProcessInstanceId());
        List<FlowProcessInstanceTraceVo> volist = flowProcessInstanceTraceRepository.getProcessInstanceTracesWithoutUserCheck(processInstanceIsList);
        if (!CollectionUtils.isEmpty(volist)) {
            FlowProcessInstanceTraceVo vo = volist.get(0);
            // 当前操作是暂停状态并且查询该流程实例已是暂停状态,则报错提示
            if (Objects.equals(httpProcessHandleDto.getAction(), ProcessInstanceActionEnum.PAUSE.getCode()) &&
                    Objects.equals(vo.getStatus(), ProcessInstanceActionEnum.PAUSE.getCode())) {
                throw new BusiException("该流程实例已经处于暂停状态");
            }

            // 当前操作是激活状态并且查询该流程实例已不是状态,则报错提示
            if (Objects.equals(httpProcessHandleDto.getAction(), ProcessInstanceActionEnum.UNPAUSE.getCode()) &&
                    !Objects.equals(vo.getStatus(), ProcessInstanceActionEnum.PAUSE.getCode())) {
                throw new BusiException("该流程实例已经处于激活状态");
            }
        }

        if (StringUtils.isBlank(httpProcessHandleDto.getComment())) {
            boolean isNeed = checkIsRequiredComment(httpProcessHandleDto.getTaskId());
            if (isNeed) {
                throw new BusiException(ErrorCode.COMMENT_NOT_EXIST);
            }
        }

        // 添加暂停描述在前
        this.addZTComment(httpProcessHandleDto);

        // 将请求DTO实体转换成VO实体
        ProcessInstanceActionVo actionVo = this.convertToProcessInstanceActionVo(httpProcessHandleDto, httpProcessHandleDto.getAction());
        boolean doAction = processInstanceMonitorService.doActionWithoutUserCheck(actionVo);
        // 如果操作成功,则添加激活原因
        this.addJHComment(doAction, httpProcessHandleDto);
        return doAction;
    }

    @Override
    public boolean handleTaskByAction(HttpProcessHandleDto httpProcessHandleDto) throws Exception {
        if (StringUtils.isBlank(httpProcessHandleDto.getTaskId())) {
            throw new BusiException("任务ID不能为空");
        }

        if (StringUtils.isBlank(httpProcessHandleDto.getUserCode())) {
            throw new BusiException("人员编码不能为空");
        }

        if (StringUtils.isBlank(httpProcessHandleDto.getAction())) {
            throw new BusiException("操作编码不能为空");
        }

        if (Objects.isNull(ProcessInstanceActionEnum.fromCode(httpProcessHandleDto.getAction()))) {
            throw new BusiException(ErrorCode.ACTION_NOT_EXIST);
        }

        // 查询任务记录
        FlowTaskTraceVo flowTaskTraceVo = flowTaskTraceRepository.getTaskTraceByTaskId(httpProcessHandleDto.getTaskId());
        if (Objects.isNull(flowTaskTraceVo)) {
            throw new BusiException(ErrorCode.TASK_NOT_EXIST);
        }

        // 当前操作是暂停状态并且查询该任务实例已是暂停状态,则报错提示
        if (Objects.equals(httpProcessHandleDto.getAction(), ProcessInstanceActionEnum.PAUSE.getCode()) &&
                Objects.equals(flowTaskTraceVo.getStatus(), ProcessInstanceActionEnum.PAUSE.getCode())) {
            throw new BusiException("该任务实例已经处于暂停状态");
        }

        // 当前操作是激活状态并且查询该任务实例已不是状态,则报错提示
        if (Objects.equals(httpProcessHandleDto.getAction(), ProcessInstanceActionEnum.UNPAUSE.getCode()) &&
                !Objects.equals(flowTaskTraceVo.getStatus(), ProcessInstanceActionEnum.PAUSE.getCode())) {
            throw new BusiException("该任务实例已经处于激活状态");
        }

        if (StringUtils.isBlank(httpProcessHandleDto.getComment())) {
            boolean isNeed = checkIsRequiredComment(httpProcessHandleDto.getTaskId());
            if (isNeed) {
                throw new BusiException(ErrorCode.COMMENT_NOT_EXIST);
            }
        }

        // 添加暂停描述在前
        this.addZTComment(httpProcessHandleDto);

        // 将请求DTO实体转换成VO实体
        ProcessInstanceActionVo actionVo = this.convertToProcessInstanceActionVo(httpProcessHandleDto, httpProcessHandleDto.getAction());

        // 操作任务实例
        boolean doAction = processInstanceMonitorService.doActionForTask(flowTaskTraceVo, actionVo);

        // 如果操作成功,则添加激活原因
        this.addJHComment(doAction, httpProcessHandleDto);
        return doAction;
    }

    /**
     * 将请求DTO实体转换成VO实体
     *
     * @param httpProcessHandleDto
     * @param action
     * @return
     */
    private ProcessInstanceActionVo convertToProcessInstanceActionVo(HttpProcessHandleDto httpProcessHandleDto, String action) {
        Set<String> processInstanceIds = new HashSet<>();
        processInstanceIds.add(httpProcessHandleDto.getProcessInstanceId());
        ProcessInstanceActionVo actionVo = new ProcessInstanceActionVo();
        actionVo.setProcessInstanceIds(processInstanceIds);
        actionVo.setTaskId(httpProcessHandleDto.getTaskId());
        actionVo.setAction(action);
        actionVo.setUserCode(httpProcessHandleDto.getUserCode());
        actionVo.setUserName(httpProcessHandleDto.getUserName());
        actionVo.setComment(httpProcessHandleDto.getComment());
        return actionVo;
    }

    private boolean checkIsRequiredComment(String taskId) {
        boolean need = false;
        FlowTaskTraceVo flowTaskTraceVo = flowTaskTraceRepository.getTaskTraceByTaskId(taskId);
        if (Objects.nonNull(flowTaskTraceVo)) {
            String processInstanceId = flowTaskTraceVo.getProcessInstanceId();
            need = nodeCandidateService.isRequiredComment(processInstanceId, flowTaskTraceVo.getActivityId());
        }
        return need;
    }

    /**
     * 添加暂停操作原因
     *
     * @param httpProcessHandleDto
     */
    private void addZTComment(HttpProcessHandleDto httpProcessHandleDto) {
        try {
            if (Objects.equals(httpProcessHandleDto.getAction(), ProcessInstanceActionEnum.PAUSE.getCode())) {
                taskService.addComment(httpProcessHandleDto.getTaskId(), httpProcessHandleDto.getProcessInstanceId(), CommentTypeEnum.ZT.getName(), httpProcessHandleDto.getComment());
            }
        } catch (Exception ex) {
            log.error("添加操作原因时失败,失败原因：{}", ex.getMessage(), ex);
        }
    }

    /**
     * 添加激活操作原因
     *
     * @param doAction
     * @param httpProcessHandleDto
     */
    private void addJHComment(boolean doAction, HttpProcessHandleDto httpProcessHandleDto) {
        try {
            if (doAction && Objects.equals(httpProcessHandleDto.getAction(), ProcessInstanceActionEnum.UNPAUSE.getCode())) {
                taskService.addComment(httpProcessHandleDto.getTaskId(), httpProcessHandleDto.getProcessInstanceId(), CommentTypeEnum.JH.getName(), httpProcessHandleDto.getComment());
            }
        } catch (Exception ex) {
            log.error("添加操作原因时失败,失败原因：{}", ex.getMessage(), ex);
        }
    }

    /**
     * 校验是否有流程发起人撤销的配置
     *
     * @param processInstanceId
     * @param participantId
     * @return
     */
    private boolean checkHasStarterLimit(String processInstanceId, String participantId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (Objects.nonNull(processInstance)) {
            List<Execution> executionList = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list();
            if (Objects.nonNull(executionList) && !executionList.isEmpty()) {
                String executionId = executionList.get(0).getId();
                Object initiatorObj = runtimeService.getVariable(executionId, "assigned_apply_initiator");
                if (Objects.nonNull(initiatorObj) && (initiatorObj instanceof String)) {
                    //获取流程发起人
                    String initiator = String.valueOf(initiatorObj);

                    String processDefinitionId = processInstance.getProcessDefinitionId();
                    ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
                    String deploymentId = processDefinition.getDeploymentId();
                    List<ModelCustomConfigDomain> modelCustomConfigDomainList = flowReModelCustomConfigRepository
                            .listModelConfigByDeployIdAndType(deploymentId, processDefinition.getKey(), ConstanceVariable.CANCEL_AUTH);
                    if (Objects.nonNull(modelCustomConfigDomainList) && !modelCustomConfigDomainList.isEmpty()) {
                        String value = modelCustomConfigDomainList.get(0).getConfig();
                        if (Objects.equals("1", value) && !Objects.equals(initiator, participantId)) {
                            return true;
                        }
                    }
                }
            }
        }

        return false;

    }


    @Override
    public HttpProcessGraphResponseDto generateProcessDiagram(String processInstanceId) {
        FlowProcessInstanceTraceVo pi = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        if (Objects.isNull(pi)) {
            return null;
        }
        HttpProcessGraphResponseDto result = new HttpProcessGraphResponseDto();
        result.setProcessInstanceId(processInstanceId);
        result.setModelName(pi.getModelName());
        result.setProcessDiagram(resolveProcessDiagram(pi));
        return result;
    }

    @Override
    public HttpProcessXmlResponseDto generateProcessXml(String processInstanceId) {
        FlowProcessInstanceTraceVo pi = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        if (Objects.isNull(pi)) {
            return null;
        }
        HttpProcessXmlResponseDto result = new HttpProcessXmlResponseDto();
        result.setProcessInstanceId(processInstanceId);
        result.setModelName(pi.getModelName());
        result.setProcessDiagram(resolveProcessXml(pi));
        return result;
    }

    private String resolveProcessXml(FlowProcessInstanceTraceVo pi) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        if (Objects.isNull(bpmnModel)) {
            return null;
        }
        byte[] bytes = new BpmnXMLConverter().convertToXML(bpmnModel);
        return Base64.getEncoder().encodeToString(bytes);
    }

    private String resolveProcessDiagram(FlowProcessInstanceTraceVo pi) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
        if (Objects.isNull(bpmnModel)) {
            return null;
        }
        List<Task> tasks = taskService.createTaskQuery().processInstanceId(pi.getProcessInstanceId()).list();
        List<String> activityIds = tasks.stream().map(TaskInfo::getTaskDefinitionKey).collect(Collectors.toList());
        InputStream inputStream = new DefaultProcessDiagramGenerator().generateDiagram(bpmnModel, "png", activityIds, new ArrayList<String>(), "宋体", "宋体", "宋体", null, 1.0);
        byte[] bytes = IoUtil.readBytes(inputStream, true);
        return Base64.getEncoder().encodeToString(bytes);
    }

    private FormInstanceDto resolveFormInstance(ProcessInstance processInstance, Map<String, Object> variables) {
        FormInstanceDto formInstance = new FormInstanceDto();
        formInstance.setProcessInstanceId(processInstance.getProcessInstanceId());
        formInstance.setResource(JSONObject.parseObject(JSONObject.toJSONString(variables)));
        return formInstance;
    }

    private void resolveEmployeeCode(ProcessInstanceDetailBaseInfoVo baseInfoVo) {
        SelectUserInfoReqVO selectUserInfoReqVO = new SelectUserInfoReqVO();
        selectUserInfoReqVO.setUserId(Long.parseLong(baseInfoVo.getApplyUserId()));
        BaseResult<RpcUserInfoVO> result = null;
        try {
            result = userFeignService.qryUserInfo(selectUserInfoReqVO);
        } catch (Exception ex) {
            log.error("error call qryUserInfo from user-center, employee code will be null", ex);
            return;
        }
        RpcUserInfoVO rpcUserInfoVO = result.getData();
        if (rpcUserInfoVO != null) {
            baseInfoVo.setApplyUserName(rpcUserInfoVO.getUserName());
            baseInfoVo.setApplyUserName(baseInfoVo.getApplyUserName() + "(" + rpcUserInfoVO.getUserAccount() + ")");
            UserEmployeeResVO userEmployeeResVO = rpcUserInfoVO.getEmployeeInfo();
            if (userEmployeeResVO != null) {
                baseInfoVo.setEmployeeCode(userEmployeeResVO.getEmployeeCode());
            }
        }
    }

    /**
     * 在流程实例中动态新增节点
     *
     * @param httpDynamicInjectTaskDto
     * @return
     */
    @Override
    public ResponseResult dynamicInjectTask(HttpDynamicInjectTaskDto httpDynamicInjectTaskDto) {
        // 当前流程实例ID
        String processInstanceId = httpDynamicInjectTaskDto.getProcessInstanceId();
        // 当前元素ID
        String currentFlowElementId = httpDynamicInjectTaskDto.getCurrentElementId();
        // 节点处理人
        String operatorId = httpDynamicInjectTaskDto.getOperatorId();
        // 动态节点名称
        String dynamicNodeName = StringUtils.isNotEmpty(httpDynamicInjectTaskDto.getDynamicNodeName()) ? httpDynamicInjectTaskDto.getDynamicNodeName() : "动态新增节点";
        // 校验字段必填
        if (StringUtils.isBlank(processInstanceId)) {
            return ResponseResult.fail(ErrorCode.PROCESS_ID_IS_NULL.errCode(), ErrorCode.PROCESS_ID_IS_NULL.errMessage() + ":[processInstanceId]");
        }

        // 校验字段必填
        if (StringUtils.isBlank(operatorId)) {
            return ResponseResult.fail(ErrorCode.PARTICIPANTID_NOT_EXIST.errCode(), ErrorCode.PARTICIPANTID_NOT_EXIST.errMessage() + ":[operatorId]");
        }

        // 校验字段必填
        if (StringUtils.isBlank(currentFlowElementId)) {
            return ResponseResult.fail(ErrorCode.ELEMENT_ID_IS_NULL.errCode(), ErrorCode.ELEMENT_ID_IS_NULL.errMessage() + ":[currentElementId]");
        }
        // 校验字段必填
        if (httpDynamicInjectTaskDto.getIsMultiInstanceTask() && Objects.isNull(httpDynamicInjectTaskDto.getMultiInstanceTaskDto())) {
            return ResponseResult.fail(ErrorCode.MULTI_CONFIG_IS_NULL.errCode(), ErrorCode.MULTI_CONFIG_IS_NULL.errMessage() + ":[multiInstanceTaskDto]");
        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();

        httpDynamicInjectTaskDto.setDynamicUserId("UserTaskAdd" + RandomStringUtils.random(8, true, true));
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());
        Process process = bpmnModel.getProcesses().get(0);
        // 校验当前节点是否存在于流程模板当中
        if (!FlowUtils.getInstance().checkCurrentElementIdValid(process, currentFlowElementId)) {
            return ResponseResult.fail(ErrorCode.ELEMENT_NOT_EXIST.errCode(), ErrorCode.ELEMENT_NOT_EXIST.errMessage() + ":" + currentFlowElementId);
        }
        try {
            processEngine.getManagementService().executeCommand(new DynamicInjectUserTaskInProcessInstanceService(httpDynamicInjectTaskDto, process.getFlowElement(currentFlowElementId)));
            supplementModelData(processInstanceId, operatorId, httpDynamicInjectTaskDto.getOperatorName());
        } catch (Exception e) {
            e.printStackTrace();
            logger.error("dynamic inject node fail, fail reason:" + e.getMessage());
            return ResponseResult.fail(ErrorCode.ADD_SIGN_TASK_FAIL.errCode(), ErrorCode.ADD_SIGN_TASK_FAIL.errMessage());
        }

        return ResponseResult.success("节点创建成功");
    }

    /**
     * 补充处理模板配置信息，并更新流程实例的对应的模型
     *
     * @param processInstanceId
     */
    private void supplementModelData(String processInstanceId, String operatorId, String operatorName) {
        FlowProcessInstanceTraceVo processInstanceTrace = flowProcessInstanceTraceRepository.getProcessInstanceTraceByProcessInstanceId(processInstanceId);
        ProcessDefinitionEntity sourceProcessDefinitionEntity = (ProcessDefinitionEntity) processEngine.getRepositoryService().getProcessDefinition(processInstanceTrace.getProcessDefinitionId());

        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        BpmnModel targetModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId());

        InputStream inputStream = repositoryService.getResourceAsStream(sourceProcessDefinitionEntity.getDeploymentId(), sourceProcessDefinitionEntity.getResourceName() + ".json");
        ProcessModelVo sourceProcessModel = ModelUtils.getInstance().streamToModel(inputStream);

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).singleResult();
        ModelUtils.getInstance().replaceBPMNModel(sourceProcessModel, targetModel);
        String dbModel = JSONObject.toJSONString(sourceProcessModel);
        byte[] modelBytes = dbModel.getBytes();
        // 重写模板配置信息
        JsonNode modelJson = JsonUtils.readTree(dbModel);
        CustomBpmnJsonConverter bpmnJsonConverter = new CustomBpmnJsonConverter();
        CustomUserTaskJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        CustomScriptTaskJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        CustomServiceTaskJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        CustomSubProcessJsonConverter.fillTypes(CustomBpmnJsonConverter.convertersToBpmnMap(), CustomBpmnJsonConverter.convertersToJsonMap());
        DeployIdContainer.setDeployId(processDefinition.getDeploymentId());
        targetModel = bpmnJsonConverter.convertToBpmnModel(modelJson);
        ModelDeploymentContext context = ModelDeploymentContext.builder()
                .deployId(processDefinition.getDeploymentId())
                .modelJson(modelJson)
                .modelId("modelId")
                .processName(processDefinition.getResourceName())
                .modelBytes(modelBytes)
                .model(null)
                .bpmnModel(targetModel)
                .build();
        for (ModelDeploymentPostProcessor modelDeploymentPostProcessor : modelDeploymentPostProcessors) {
            try {
                modelDeploymentPostProcessor.process(context);
            } catch (Exception e) {
                logger.info("ModelDeploymentPostProcessor Process Error, Reason:" + e.getLocalizedMessage());
                e.printStackTrace();
            }
        }
        // 更新流程实例流转信息表
        flowProcessInstanceTraceRepository.updateProcDefId(processInstanceId, processDefinition.getId(), processInstanceTrace.getVersion());
        // 4.新增操作记录
        FlowProcessInstanceOperateTraceVo operateTrace = new FlowProcessInstanceOperateTraceVo("dynamicInjectTask", processInstanceId, null, operatorId, operatorName,
                processInstanceTrace.getProcessDefinitionId() + "_更新至_" + processDefinition.getId());
        flowProcessInstanceOperateTraceRepository.insertProcessInstanceOperateTrace(operateTrace);
    }

    /**
     * 将流程实例迁移至流程最新版本
     *
     * @param httpMigrateProcessModelDto
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult migrateToNewVersion(HttpMigrateProcessModelDto httpMigrateProcessModelDto) {
        // 当前流程实例ID
        String processInstanceId = httpMigrateProcessModelDto.getProcessInstanceId();

        // 校验字段必填
        if (StringUtils.isBlank(processInstanceId)) {
            return ResponseResult.fail(ErrorCode.PROCESS_ID_IS_NULL.errCode(), ErrorCode.PROCESS_ID_IS_NULL.errMessage() + ":[processInstanceId]");
        }
        // 节点处理人
        String operatorId = httpMigrateProcessModelDto.getOperatorId();
        // 校验字段必填
        if (StringUtils.isBlank(operatorId)) {
            // 添加默认操作人：
            User user = userRepository.getUserByName("superAdmin");
            if (null != user) {
                httpMigrateProcessModelDto.setOperatorId(user.getUserId());
                httpMigrateProcessModelDto.setOperatorName(user.getUserName());
            }

        }
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        Process oldProcess = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()).getMainProcess();
        // 查询流程模板的最新版本
        Model latestModel = repositoryService.createModelQuery().modelKey(oldProcess.getId()).latestVersion().singleResult();
        ProcessDefinition latestProcessDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(latestModel.getDeploymentId()).singleResult();
        Process latestProcess = repositoryService.getBpmnModel(latestProcessDefinition.getId()).getMainProcess();
        if (processInstance.getProcessDefinitionId().equals(latestProcessDefinition.getId())) {
            return ResponseResult.fail(ErrorCode.BAD_REQUEST.errCode(), "当前流程为最新版本，无需迁移!");
        }
        // 获取流程实例当前任务节点编码集合
        Set<String> currentTaskCodes = getCurrentTaskCode(processInstanceId);
        // 校验当前节点是否存在于最新版本流程模板当中
        for (String currentElementId : currentTaskCodes) {
            if (!FlowUtils.getInstance().checkCurrentElementIdValid(latestProcess, currentElementId)) {
                return ResponseResult.fail(ErrorCode.ELEMENT_NOT_EXIST.errCode(), ErrorCode.ELEMENT_NOT_EXIST.errMessage() + "【" + currentElementId + "】，无法迁移至最新版本!");
            }
        }
        // 1.更新Execution
        flowTaskTraceDAO.updateExecutionByProcId(processInstanceId, latestProcessDefinition.getId());
        // 2.更新Task
        flowTaskTraceDAO.updateTaskByProcId(processInstanceId, latestProcessDefinition.getId());
        // 3.更新FlowProcessTrace
        flowProcessInstanceTraceRepository.updateProcDefId(processInstanceId, latestProcessDefinition.getId(), latestModel.getVersion());
        // 4.新增操作记录
        FlowProcessInstanceOperateTraceVo operateTrace = new FlowProcessInstanceOperateTraceVo("migrateToNewVersion", processInstanceId, null, httpMigrateProcessModelDto.getOperatorId(), httpMigrateProcessModelDto.getOperatorName(),
                processInstance.getProcessDefinitionId() + "_升级至最新版本_" + latestProcessDefinition.getId());
        flowProcessInstanceOperateTraceRepository.insertProcessInstanceOperateTrace(operateTrace);
        return ResponseResult.success(processInstance.getProcessDefinitionId() + "已升级至最新版本！" + latestProcessDefinition.getId());
    }

    /**
     * 获取流程实例当前任务节点编码集合
     *
     * @param processInstanceId
     * @return
     */
    public Set<String> getCurrentTaskCode(String processInstanceId) {
        List<String> currentTaskList = taskService.createTaskQuery().processInstanceId(processInstanceId).list().stream().map(task -> task.getTaskDefinitionKey()).collect(Collectors.toList());
        HashSet<String> taskCodes = new HashSet<>(currentTaskList);
        return taskCodes;
    }

    /**
     * 重启被终止的流程
     *
     * @param httpProcessHandleDto
     * @return
     */
    @Override
    public ResponseResult restartProcess(HttpProcessHandleDto httpProcessHandleDto) {

        String processInstanceId = httpProcessHandleDto.getProcessInstanceId();
        HistoricProcessInstance terminatedInstance = historyService.createHistoricProcessInstanceQuery().deleted().processInstanceId(processInstanceId).singleResult();
        if (!Objects.isNull(terminatedInstance)) {
//                    获取已终止流程实例的相关信息，如流程定义ID、业务关联键等。
            String processDefinitionId = terminatedInstance.getProcessDefinitionId();
            String businessKey = terminatedInstance.getBusinessKey();
            List<HistoricTaskInstance> historicTaskInstances = historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list();
            List<HistoricVariableInstance> historicVariableInstances = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list();
        }


        return ResponseResult.success(httpProcessHandleDto.getProcessInstanceId()+"重启成功！");
    }

    private static List<String> nodeList;
    private static Map<String,String> nodeMap;
    static {
        nodeList = new ArrayList<>();// 利用list有序原理，后续修改节点审批人（运维工具）功能需要
        nodeList.add("FEEDBACK_02");// 编制人3
        nodeList.add("FEEDBACK_03");// 编制人2
        nodeList.add("FEEDBACK_04");// 编制人1
        nodeList.add("FEEDBACK_05");// 评价人
        nodeMap = new HashMap<>();
        nodeMap.put("FEEDBACK_02","ASSIGN3");// 编制人3
        nodeMap.put("FEEDBACK_03","ASSIGN2");// 编制人2
        nodeMap.put("FEEDBACK_04","ASSIGN1");// 编制人1
        nodeMap.put("FEEDBACK_05","EVALPERS");// 评价人
    }

    public String queryCurrentNodeByFlowId(HttpModifyApproverVO modifyApproverVO){
        Map paramMap = new HashMap();
        modifyApproverVO.getFlowIds().forEach(flowId->{
            HttpDispatchWorkItemDto batchTurnTaskVo = new HttpDispatchWorkItemDto();
            paramMap.clear();
            paramMap.put("flowId",flowId);
            String activityId = flowTaskTraceDAO.queryCurrentNodeByFlowId(flowId);
            nodeMap.get(activityId);
            int indexOf = nodeList.indexOf(activityId);
            String taskid = flowTaskTraceDAO.querytaskIdByFlowId(flowId);
            batchTurnTaskVo.setWorkItemId(taskid);
            batchTurnTaskVo.setComment("运维工具转办");
            if ("FEEDBACK_02".equals(activityId)){
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign3())){ // 当前节点需通过转办来进行修改
                    batchTurnTaskVo.setTurnToUserCode(modifyApproverVO.getNewAssign3());
                    flowTurnTaskService.turnTaskNonUserCheck(batchTurnTaskVo);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign2())){
                    paramMap.put("approver",modifyApproverVO.getNewAssign2());//指派人2
                    paramMap.put("node","ASSIGN2");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign1())){
                    paramMap.put("approver",modifyApproverVO.getNewAssign1());//指派人1
                    paramMap.put("node","ASSIGN1");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewEvalpers())){
                    paramMap.put("approver",modifyApproverVO.getNewEvalpers());//评价人
                    paramMap.put("node","EVALPERS");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
            } else if ("FEEDBACK_03".equals(activityId)){
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign2())){ // 当前节点需通过转办来进行修改
                    batchTurnTaskVo.setTurnToUserCode(modifyApproverVO.getNewAssign2());
                    flowTurnTaskService.turnTaskNonUserCheck(batchTurnTaskVo);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign1())){
                    paramMap.put("approver",modifyApproverVO.getNewAssign1());//指派人1
                    paramMap.put("node","ASSIGN1");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewEvalpers())){
                    paramMap.put("approver",modifyApproverVO.getNewEvalpers());//评价人
                    paramMap.put("node","EVALPERS");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
            } else if ("FEEDBACK_04".equals(activityId)){
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign1())){ // 当前节点需通过转办来进行修改
                    batchTurnTaskVo.setTurnToUserCode(modifyApproverVO.getNewAssign1());
                    flowTurnTaskService.turnTaskNonUserCheck(batchTurnTaskVo);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewEvalpers())){
                    paramMap.put("approver",modifyApproverVO.getNewEvalpers());//评价人
                    paramMap.put("node","EVALPERS");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
            } else if ("FEEDBACK_05".equals(activityId)){
                if (StringUtils.isNotBlank(modifyApproverVO.getNewEvalpers())){ // 当前节点需通过转办来进行修改
                    batchTurnTaskVo.setTurnToUserCode(modifyApproverVO.getNewEvalpers());
                    flowTurnTaskService.turnTaskNonUserCheck(batchTurnTaskVo);
                }
            } else if ("FEEDBACK_07".equals(activityId)){
                // 0级PEMO节点，暂不做业务处理
            } else if ("FEEDBACK_01".equals(activityId)){// 反馈节点
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign3())){
                    paramMap.put("approver",modifyApproverVO.getNewAssign3());//指派人3
                    paramMap.put("node","ASSIGN3");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign2())){
                    paramMap.put("approver",modifyApproverVO.getNewAssign2());//指派人2
                    paramMap.put("node","ASSIGN2");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewAssign1())){
                    paramMap.put("approver",modifyApproverVO.getNewAssign1());//指派人1
                    paramMap.put("node","ASSIGN1");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
                if (StringUtils.isNotBlank(modifyApproverVO.getNewEvalpers())){
                    paramMap.put("approver",modifyApproverVO.getNewEvalpers());//评价人
                    paramMap.put("node","EVALPERS");//审批节点
                    flowTaskTraceDAO.updateApprover(paramMap);
                }
            }
        });
        return "success";
    }

}
